﻿#include "GxResizeProc.h"
#include <QHoverEvent>
#include <QMouseEvent>
#include <QApplication>
#include <QDesktopWidget>
#include <QDebug>
#include <QTime>


GxResizeProc::GxResizeProc(QWidget *parent)
    : QObject(parent)
    , m_bResizable(false)
{

    setResizable(m_bResizable);
	parent->setAttribute(Qt::WA_Hover, true);
	m_iDragMargin = 8;					// 可缩放的边距
	m_bMousePressed = false;			// 标记鼠标是否按下
	m_mouseStyle = NORMAL;				// 鼠标样式
	m_iDragXPadding = 0;				// 拖动时记录x边距
	m_iDragYPadding = 0;				// 拖动时记录y边距
    parent->installEventFilter(this);
}

void GxResizeProc::setResizable(bool bResizable)
{
    m_bResizable = bResizable;
}

void GxResizeProc::setResizeBorderWidth(int iWidth)
{
    if(iWidth > 0)
    {
        m_iDragMargin = iWidth;
    }
}

void GxResizeProc::changeMouseStyle(const QPoint &mousePos)
{
    QWidget *q = qobject_cast<QWidget*>(parent());
    if(!q)
        return;
    if (!m_bResizable || q->windowState().testFlag(Qt::WindowMaximized) || q->windowState().testFlag(Qt::WindowFullScreen))
    {
        m_mouseStyle = NORMAL;
        q->setCursor(Qt::ArrowCursor);
        return;
    }

    bool bWidthCanResize = q->minimumWidth() < q->maximumWidth();
    bool bHeightCanResize = q->minimumHeight() < q->maximumHeight();

    int iPosX = mousePos.x();
    int iPosY = mousePos.y();

    int iWidth = q->width();
    int iHeight = q->height();

    m_topLeftPos = q->geometry().topLeft();
    m_bottomRightPos = q->geometry().bottomRight();

    if (bWidthCanResize && bHeightCanResize)
    {
        // 左下
        if (iPosX >= 0 && iPosX <= m_iDragMargin &&
            iPosY <= iHeight && iPosY >= iHeight - m_iDragMargin)
        {
            m_iDragXPadding = iPosX;
            m_iDragYPadding = iHeight - iPosY;
            q->setCursor(Qt::SizeBDiagCursor);
            m_mouseStyle = BOTTOMLEFT;
            return;
        }
        // 右下
        if (iPosX <= iWidth && iPosX >= iWidth - m_iDragMargin &&
            iPosY <= iHeight && iPosY >= iHeight - m_iDragMargin)
        {
            m_iDragXPadding = iWidth - iPosX;
            m_iDragYPadding = iHeight - iPosY;
            q->setCursor(Qt::SizeFDiagCursor);
            m_mouseStyle = BOTTOMRIGHT;
            return;
        }
        // 左上
        if (iPosX >= 0 && iPosX <= m_iDragMargin &&
            iPosY >= 0 && iPosY <= m_iDragMargin)
        {
            m_iDragXPadding = iPosX;
            m_iDragYPadding = iPosY;
            q->setCursor(Qt::SizeFDiagCursor);
            m_mouseStyle = TOPLEFT;
            return;
        }
        // 右上
        if (iPosX <= iWidth && iPosX >= iWidth - m_iDragMargin &&
            iPosY >= 0 && iPosY <= m_iDragMargin)
        {
            m_iDragXPadding = iWidth - iPosX;
            m_iDragYPadding = iPosY;
            q->setCursor(Qt::SizeBDiagCursor);
            m_mouseStyle = TOPRIGHT;
            return;
        }
    }
    if (bWidthCanResize)
    {
        // 左
        if (iPosX >= 0 && iPosX <= m_iDragMargin)
        {
            m_iDragXPadding = iPosX;
            q->setCursor(Qt::SizeHorCursor);
            m_mouseStyle = LEFT;
            return;
        }
        // 右
        if (iPosX <= iWidth && iPosX >= iWidth - m_iDragMargin)
        {
            m_iDragXPadding = iWidth - iPosX;
            q->setCursor(Qt::SizeHorCursor);
            m_mouseStyle = RIGHT;
            return;
        }
    }
    if (bHeightCanResize)
    {
        // 下
        if (iPosY <= iHeight && iPosY >= iHeight - m_iDragMargin)
        {
            m_iDragYPadding = iHeight - iPosY;
            q->setCursor(Qt::SizeVerCursor);
            m_mouseStyle = BOTTOM;
            return;
        }
        // 上
        if (iPosY >= 0 && iPosY <= m_iDragMargin)
        {
            m_iDragYPadding = iPosY;
            q->setCursor(Qt::SizeVerCursor);
            m_mouseStyle = TOP;
            return;
        }
    }
    q->setCursor(Qt::ArrowCursor);
    m_mouseStyle = NORMAL;
}


bool GxResizeProc::eventFilter(QObject *watched, QEvent *event)
{
    if(!m_bResizable)
        return false;
    QWidget *q = qobject_cast<QWidget*>(parent());
    if(!q)
        return false;
    if (watched != q || !q->isWindow())
    {
        return QObject::eventFilter(watched, event);
    }

    switch (event->type())
    {
        
        // [1]Hover移动 begin
        case QEvent::HoverMove:
        {
            
            QHoverEvent *pHovEvt = dynamic_cast<QHoverEvent *>(event);
            // 如果鼠标没有按下，则根据当前鼠标位置设置鼠标样式
            if (!m_bMousePressed)
            {
                changeMouseStyle(pHovEvt->pos());
            }
            else
            {
                //FIXED: H00015 修复QHoverEvent的pos转全局坐标后的跳动问题
                QPoint globalMousePos = QCursor::pos();
                //qDebug() << "mouse" << globalMousePos;

                // 鼠标是正常样式时，拖动界面
                if (m_mouseStyle == GxResizeProc::NORMAL)
                {
                    QRect availGeo = QApplication::desktop()->availableGeometry(QApplication::desktop()->screenNumber(globalMousePos));
                    if (q->windowState().testFlag(Qt::WindowMaximized) || q->windowState().testFlag(Qt::WindowFullScreen))
                    {
                        qDebug() << "maximized or full screen";
                        QRect normalGeo = q->normalGeometry();
                        q->showNormal();
                        m_mousePos.setX(m_mousePos.x() * 1.0 * normalGeo.width() / availGeo.width());
                        if (m_mousePos.y() >= normalGeo.height())
                        {
                            m_mousePos.setY(m_mousePos.y() * 1.0 * normalGeo.height() / availGeo.height());
                        }
                    }
                    //FIXME: 逻辑有问题
                    else
                    {
                        //q->setGeometry(m_normalGeo);
                    }
                    globalMousePos.setX(qBound(availGeo.x(), globalMousePos.x(), availGeo.x() + availGeo.width()));
                    globalMousePos.setY(qBound(availGeo.y(), globalMousePos.y(), availGeo.y() + availGeo.height()));
                    q->move(globalMousePos - m_mousePos);
                    
                    return QObject::eventFilter(watched, event);
                }
                // 否则，进行缩放
                if (!m_bResizable)
                {
                    return QObject::eventFilter(watched, event);
                }
                
                int iMinimumHeight = q->minimumHeight();
                int iMinimumWidth = q->minimumWidth();
                switch (m_mouseStyle)
                {
                    case GxResizeProc::BOTTOM:
                    {
                        if (globalMousePos.y() - m_topLeftPos.y() > iMinimumHeight)
                        {
                            m_bottomRightPos.setY(globalMousePos.y() + m_iDragYPadding);
                        }
                        else
                        {
                            m_bottomRightPos.setY(m_topLeftPos.y() + iMinimumHeight);
                        }
                    }
                    break;

                    case GxResizeProc::RIGHT:
                    {
                        if (globalMousePos.x() - m_topLeftPos.x() > iMinimumWidth)
                        {
                            m_bottomRightPos.setX(globalMousePos.x() + m_iDragXPadding);
                        }
                        else
                        {
                            m_bottomRightPos.setX(m_topLeftPos.x() + iMinimumWidth);
                        }
                    }
                    break;
                    case GxResizeProc::BOTTOMRIGHT:
                    {
                        if (globalMousePos.x() - m_topLeftPos.x() > iMinimumWidth)
                        {
                            m_bottomRightPos.setX(globalMousePos.x() + m_iDragXPadding);
                        }
                        else
                        {
                            m_bottomRightPos.setX(m_topLeftPos.x() + iMinimumWidth);
                        }
                        if (globalMousePos.y() - m_topLeftPos.y() > iMinimumHeight)
                        {
                            m_bottomRightPos.setY(globalMousePos.y() + m_iDragYPadding);
                        }
                        else
                        {
                            m_bottomRightPos.setY(m_topLeftPos.y() + iMinimumHeight);
                        }
                    }
                    break;

                    case GxResizeProc::TOP:
                    {
                        if (m_bottomRightPos.y() - globalMousePos.y() > iMinimumHeight)
                        {
                            m_topLeftPos.setY(globalMousePos.y() - m_iDragYPadding);
                        }
                        else
                        {
                            m_topLeftPos.setY(m_bottomRightPos.y() - iMinimumHeight);
                        }
                    }
                    break;

                    case GxResizeProc::LEFT:
                    {
                        if (m_bottomRightPos.x() - globalMousePos.x() > iMinimumWidth)
                        {
                            m_topLeftPos.setX(globalMousePos.x() - m_iDragXPadding);
                        }
                        else
                        {
                            m_topLeftPos.setX(m_bottomRightPos.x() - iMinimumWidth);
                        }
                    }
                    break;

                    case GxResizeProc::TOPLEFT:
                    {
                        if (m_bottomRightPos.y() - globalMousePos.y() > iMinimumHeight)
                        {
                            m_topLeftPos.setY(globalMousePos.y() - m_iDragYPadding);
                        }
                        else
                        {
                            m_topLeftPos.setY(m_bottomRightPos.y() - iMinimumHeight);
                        }
                        if (m_bottomRightPos.x() - globalMousePos.x() > iMinimumWidth)
                        {
                            m_topLeftPos.setX(globalMousePos.x() - m_iDragXPadding);
                        }
                        else
                        {
                            m_topLeftPos.setX(m_bottomRightPos.x() - iMinimumWidth);
                        }
                    }
                    break;

                    case GxResizeProc::TOPRIGHT:
                    {
                        if (m_bottomRightPos.y() - globalMousePos.y() > iMinimumHeight)
                        {
                            m_topLeftPos.setY(globalMousePos.y() - m_iDragYPadding);
                        }
                        else
                        {
                            m_topLeftPos.setY(m_bottomRightPos.y() - iMinimumHeight);
                        }
                        if (globalMousePos.x() - m_topLeftPos.x() > iMinimumWidth)
                        {
                            m_bottomRightPos.setX(globalMousePos.x() + m_iDragXPadding);
                        }
                        else
                        {
                            m_bottomRightPos.setX(m_topLeftPos.x() + iMinimumWidth);
                        }
                    }
                    break;

                    case GxResizeProc::BOTTOMLEFT:
                    {
                        if (m_bottomRightPos.x() - globalMousePos.x() > iMinimumWidth)
                        {
                            m_topLeftPos.setX(globalMousePos.x() - m_iDragXPadding);
                        }
                        else
                        {
                            m_topLeftPos.setX(m_bottomRightPos.x() - iMinimumWidth);
                        }
                        if (globalMousePos.y() - m_topLeftPos.y() > iMinimumHeight)
                        {
                            m_bottomRightPos.setY(globalMousePos.y() + m_iDragYPadding);
                        }
                        else
                        {
                            m_bottomRightPos.setY(m_topLeftPos.y() + iMinimumHeight);
                        }
                    }
                    break;

                    default:
                        break;
                }
                q->setGeometry(QRect(m_topLeftPos, m_bottomRightPos));
                qApp->processEvents();
            }
            
        }
        break;
        // [1]鼠标移动 end

        // [2]鼠标按下 begin
        case QEvent::MouseButtonPress:
        {
            QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent *>(event);
            if (mouseEvent->button() == Qt::LeftButton)
            {
                if (m_mouseStyle == GxResizeProc::NORMAL)
                {
                    QPoint pressPt = mouseEvent->pos();
					//处于标题栏或者其他可拖拽区域
                    if (movable(pressPt))
                    {
                        m_bMousePressed = true;
                        m_mousePos = mouseEvent->pos();
                    }
                }
                else
                {
                    m_bMousePressed = true;
                    m_mousePos = mouseEvent->pos();
                }
            }
        }
        break;
        // [2]鼠标按下 end

        // [3]鼠标松开 begin
        case QEvent::MouseButtonRelease:
        {
            if (m_mouseStyle != GxResizeProc::NORMAL)
            {
                QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent *>(event);
                if(mouseEvent)
                {
                    changeMouseStyle(mouseEvent->pos());
                }
            }
            m_bMousePressed = false;
        }
        break;
        // [3]鼠标松开 end

        // [4]鼠标双击 begin
        case QEvent::MouseButtonDblClick:
        {
            // if (m_bResizable)
            // {
            //     QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent *>(event);
            //     QPoint pressPt = mouseEvent->pos();
            //     // 鼠标双击标题栏，进行最大化和正常大小切换
            //     if (movable(pressPt))
            //     {
            //         if (q->windowState().testFlag(Qt::WindowMaximized))
            //         {
            //             q->showNormal();
            //         }
            //         else if (!q->windowState().testFlag(Qt::WindowFullScreen))
            //         {
            //             q->showMaximized();
            //         }
            //     }
            // }
        }
        break;
        // [4]鼠标双击 end

		case QEvent::Leave:
		case QEvent::HoverLeave:
		{
			m_bMousePressed = false;
// 			QHoverEvent *hoverEvent = dynamic_cast<QHoverEvent *>(event);
// 			changeMouseStyle(hoverEvent->pos());
			m_mouseStyle = NORMAL;
			q->setCursor(Qt::ArrowCursor);
		}
		break;

        default:
        break;
    }
    return QObject::eventFilter(watched, event);
}

bool GxResizeProc::movable(const QPoint& p)
{
    return true;
}